%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define YYERROR_VERBOSE 1
#define ROWS 4
#define COLS 10
 
int  columns[COLS];
int  groups [COLS];
char WHERE[128];

int  isEnd=0;
int  isGroupBy=0;

int yyerror(const char *str)    /* called by yyparse on error */
{
        fprintf(stderr,"awk2sql parse error: %s\n",str);
	return 1;
}
 
int yywrap()
{
        /* fprintf(stdout,"yywrap() is called\n"); */ 
        return 1;
} 
  
void f_aggregate(int column, int typeAgg)
{
	if (column<COLS) groups[column] = column + 10 * typeAgg;
}

int f_selectivity()
{
	int k=0, sum=0;
	for (k=0; k<COLS; k++) sum+=columns[k];
	return sum;
}

int f_aggregates()
{
	int k=0, sum=0;
	for (k=0; k<COLS; k++) sum+=groups[k];
	return sum;
}

void f_columns(int column)
{
	if (column<COLS) columns[column] = column;
}

void f_where(int column, char* token)
{
	if (column>0)
	{
	   sprintf(WHERE, "WHERE %d LIKE \"%%%s%%\"", column, token);
	   columns[column]=-1;
	}
	else
	   sprintf(WHERE, "WHERE <columns> LIKE \"%%%s%%\"", token);
}

void f_generateSQL()
{
	int nAggregate = f_aggregates();
	int nColumns   = f_selectivity();

	char COLUMNS[128];
	sprintf(COLUMNS, "");
	char GROUPBY[128];
        sprintf(GROUPBY, "");
	char ORDERBY[128];
        sprintf(ORDERBY, "");
	char ROLLUP[128];

	if (nAggregate>0 && nColumns) 
	{
	    sprintf(GROUPBY, "GROUP BY (");
	    sprintf(ORDERBY, "ORDER BY");
	}

	int k=0, nC=0, nG=0;
	for (k=0; k<COLS; k++)
	{
	    if (columns[k]>0) 
	    {
		if (nC>0) sprintf(COLUMNS, "%s,", COLUMNS);
		sprintf(COLUMNS, "%s %d", COLUMNS, k); 
		nC++;

		if (nAggregate>0) 
		{
		   if (nG>0) 
		   {
			sprintf(GROUPBY, "%s,", GROUPBY);
			sprintf(ORDERBY, "%s,", ORDERBY);
		   }
			
                   sprintf(GROUPBY, "%s %d", GROUPBY, k);
                   sprintf(ORDERBY, "%s %d", ORDERBY, k);
		   nG++;
		}
	    }

	    if (groups[k]>=30)
	    {
        	sprintf(ROLLUP, "");
		if (nC>0) sprintf(COLUMNS, "%s,", COLUMNS);
            	sprintf(COLUMNS, "%s SUM(%d) AS SUM_%d", COLUMNS, k, k);
		nC++;
		if (nG==0) sprintf(GROUPBY, "GROUP BY");
		if (nG>0) sprintf(GROUPBY, "%s,", GROUPBY);
			
                sprintf(GROUPBY, "%s %d", GROUPBY, k);
		nG++;
	    }
	    else if (groups[k]>=20)
	    {
        	sprintf(ROLLUP, "WITH ROLLUP");
		if (nC>0) sprintf(COLUMNS, "%s,", COLUMNS);
            	sprintf(COLUMNS, "%s SUM(%d) AS SUM_%d", COLUMNS, k, k);
		nC++;
	    }
	    else if (groups[k]>=10)
	    {
        	sprintf(ROLLUP, "WITH ROLLUP");
		if (nC>0) sprintf(COLUMNS, "%s,", COLUMNS);
            	sprintf(COLUMNS, "%s COUNT(*) AS NROWS", COLUMNS);
		nC++;
	    }
	}

	if (nAggregate>0 && nColumns)
	   sprintf(GROUPBY, "%s) %s", GROUPBY, ROLLUP);

	printf("SELECT%s FROM <table> %s %s %s\n", COLUMNS, WHERE, GROUPBY, ORDERBY);

	// Data Loader...
	int nI=0;
	printf("INSERT INTO <table> VALUES(");	
	if (columns[0]!=0 || groups[0]!=0)	
	    printf("<columns>");
	else
	{
	for (k=1; k<COLS; k++)
        {
	    if (columns[k]!=0)
	    {
		if (nI>0) printf(", ");
		printf("COL_%d", k);
		nI++;
	    }
	    else if (groups[k]!=0)
	    {
		if (nI>0) printf(", ");
		printf("COL_%d", k);
		nI++;
	    }
	}
	}
	printf(");\n");	
}

int main(int argc, char **argv)
{
	int j=0;
	for (j=0; j<COLS; j++)
   	    columns[j]=groups[j]=0;

        yyparse();

	if (argc>1 && !strcmp(argv[1],"1"))
	{
	    for (j=0; j<COLS; j++)
		printf("columns[%d]=%d, groups[%d]=%d\n", j, columns[j], j, groups[j]); 
	}

	exit(0);
} 

%}

%token LBRACE RBRACE LPAREN RPAREN LSQUAR RSQUAR BBEGIN BEND PRINT COMMA DOLLAR DEC INC ADD SUB SLASH EQUAL MATCH GT LT GE LE EQ NE IF FOR IN

%left GE LE EQ NE '>' '<' 
%nonassoc '++' '--'

%union
{  
    int number;
    char* string;
    int level;
}

%token <number> NUMBER
%token <string> VAR
%type  <number> arg field array

%start program
%%

program: 
	  /* empty */ 
	| beginsection pattern body endsection { f_generateSQL(); }
	; 

beginsection : 
	  /* empty */ 
	| BBEGIN LBRACE expr RBRACE
	;

pattern : 
	  /* empty */ 
	| SLASH VAR SLASH		    { f_where(0, $2); }
	| field MATCH SLASH VAR SLASH       { f_where($1, $4); }
        ;

body : 
	  LBRACE stmt_list RBRACE
	; 

endsection : 
	  /* empty */ 
	| BEND body			    { isEnd=1; }
	;

stmt_list : 
	  stmt 
	| stmt_list stmt
	;

stmt :
	  expr; 
	| output
/*        | IF LPAREN expression RPAREN stmt { printf("IF evaluation: %d\n", $3); }  */
	| expr_array;
	| FOR LPAREN VAR IN VAR RPAREN body    { if (isEnd) isGroupBy=1; }

/* LBRACE output RBRACE */
        ;

/*
expression:
        | arg GT NUMBER      { $$ = ($1>$3);  }
        | arg LT NUMBER      { $$ = ($1<$3);  }
        | expression GE expression      { $$ = ($1>=$3); }
        | expression LE expression      { $$ = ($1<=$3); }
        | expression NE expression      { $$ = ($1!=$3); }
        | expression EQ expression      { $$ = ($1==$3); }
        | '(' expression ')'            { $$ = $2; }
        ;
*/

output : 
	  PRINT arg_list
	;

expr: 
	  DEC VAR 
	| INC VAR 
	| VAR DEC 
	| VAR INC 		   {  f_aggregate( 0, 1); }
	| VAR EQUAL NUMBER 
	| VAR EQUAL field 
	| VAR ADD DOLLAR NUMBER    { f_aggregate($4, 2); }
	| VAR ADD NUMBER           { f_aggregate( 0, 1); }
	;

arg_list : 
	  arg COMMA arg_list
	| arg
	;

arg : 
	  field			   { $$=$1; }
	| array			   { $$=$1; }
	| VAR 			   { $$=$1; }
	;

expr_array    : 
	  array INC		   { f_aggregate( 0, 3);   }
	| array ADD DOLLAR NUMBER  { f_aggregate($4, 3);   }
	;

array	:
	  VAR LSQUAR arg RSQUAR    { $$=$3; }
	;

field	: 
	  DOLLAR NUMBER		   { $$=$2; f_columns($2); }
	;
